package com.ruoyi.framework.provider;

import cn.hutool.json.JSONObject;
import com.ruoyi.framework.config.properties.DatabaseDynamicPasswordProperties;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.*;
import java.io.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Base64;

import static java.util.Objects.isNull;

/**
 * 动态密码提供者
 */
@Slf4j
@Component
public class DynamicDatabasePasswordProvider {


    //风控管理平台
    //风控管理平台
    private static final String ALGORITHM = "SM4";
    private static final String SK = "1111=";
    private static final String AK = "111111";
    private static final String skVersion = "1";
    private static final String keyId = "111111-384c-485a-9352-c84e8a0fc402";
    private static final String productCode = "SD-FKGLPT";
    private static final String token = "11111-7511-4bf2-955d-d71c00481a6b";
    private static final String institutionNumber = "11111";
    private static final String SM4_CBC_PKCS5 = "SM4/CBC/PKCS5Padding";
    private static final BouncyCastleProvider PROVIDER = new BouncyCastleProvider();

    static {
        if (isNull(Security.getProvider(BouncyCastleProvider.PROVIDER_NAME))) {
            Security.addProvider(PROVIDER);
        }
    }

    @Autowired
    private DatabaseDynamicPasswordProperties dynamicPasswordProperties;

    public static JSONObject httppost(String url, String param, String accessToken) {
        try {
            System.setProperty("jsse.enableSNIExtension", "false");
            ignoreSsl();
        } catch (KeyManagementException | NoSuchAlgorithmException e1) {
            e1.printStackTrace();
        }

        PrintWriter out = null;
        BufferedReader in = null;
        StringBuilder sb = null;
        {
            sb = new StringBuilder();
            try {
                URL realUrl = new URL(url);
                HttpsURLConnection conn = null;

                conn = (HttpsURLConnection) realUrl.openConnection();

                conn.setRequestProperty("accept", "*/*");
                conn.setRequestProperty("connection", "Keep-Alive");
                conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
                conn.setRequestProperty("Content-type", "application/json");
                conn.setRequestProperty("Authorization", "Bearer " + accessToken);
                conn.setDoOutput(true);
                conn.setDoInput(true);
                out = new PrintWriter(new OutputStreamWriter(conn.getOutputStream(), "utf-8"));
                out.print(param);
                out.flush();
                in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
                String line;
                while ((line = in.readLine()) != null) {
                    sb.append(line);
                }
            } catch (Exception e) {
                System.out.println("发送请求出现异常！" + e.getMessage());
            } finally {
                try {
                    if (out != null) {
                        out.close();
                    }
                    if (in != null) {
                        in.close();
                    }
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        }
        try {
            return new JSONObject(sb.toString());
        } catch (Exception e) {
            return null;
            // TODO: handle exception
        }
    }

    public static void ignoreSsl() throws NoSuchAlgorithmException, KeyManagementException {
        System.setProperty("jsse.enableSNIExtension", "false");
        TrustManager[] trustAllCerts = new TrustManager[]{
                new X509TrustManager() {
                    @Override
                    public void checkClientTrusted(X509Certificate[] x509Certificates,
                                                   String s) throws CertificateException {
                    }

                    @Override
                    public void checkServerTrusted(X509Certificate[] x509Certificates,
                                                   String s) throws CertificateException {
                    }

                    @Override
                    public X509Certificate[] getAcceptedIssuers() {
                        return new X509Certificate[0];
                    }
                }
        };
        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

        // 自定义hostname校验器
        HostnameVerifier allHostsValid = (hostname, session) -> true;
        HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
    }

    public static String decrypt_CBC(String data) {
        try {
            String key = SK.substring(0, 16);
            String iv = SK.substring(16, 32);
            Cipher cipher = Cipher.getInstance(SM4_CBC_PKCS5, BouncyCastleProvider.PROVIDER_NAME);
            SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), ALGORITHM);
            IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
            return new String(cipher.doFinal(Base64.getDecoder().decode(data)), StandardCharsets.UTF_8);
        } catch (NoSuchPaddingException | NoSuchAlgorithmException | NoSuchProviderException |
                 InvalidAlgorithmParameterException | InvalidKeyException | IllegalBlockSizeException |
                 BadPaddingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 从外部服务器获取数据库密码
     *
     * @return 数据库密码
     */
    public String getPassword() {
        try {
            String token = getToken();
            if (token == null) {
                throw new RuntimeException("解密失败: 获取token异常");
            }
            String pass = getPass(token);
            if (pass == null) {
                throw new RuntimeException("解密失败: 获取pass异常");
            }
            try {
                pass = decrypt_CBC(pass);
            } catch (Exception e) {
                throw new RuntimeException("解密失败: " + pass, e);
            }
            return pass;
        } catch (Exception e) {
            log.error("获取动态数据库密码失败", e);
            throw new RuntimeException("无法获取数据库密码", e);
        }
    }

    /**
     * 获取服务器token
     *
     * @return
     */
    public String getToken() {
        String url = dynamicPasswordProperties.getHost() + "/oauth2/token";
        JSONObject data = new JSONObject();
        data.set("app_key", dynamicPasswordProperties.getAppKey());
        data.set("app_secret", dynamicPasswordProperties.getAppSecret());
        JSONObject out = httppost(url, data.toString(), "xxxxxx");
        if (out == null) {
            log.error("获取token失败！{}", out);
            return null;
        } else if (out.getStr("code").equals("0")) {
            out = out.getJSONObject("result");
            String accessToken = out.getStr("access_token");
            log.info("获取token成功！{}", accessToken);
            return accessToken;
        } else {
            log.error("获取token失败！{}", out);
            return null;
        }
    }

    /**
     * 获取数据库密码
     *
     * @param accessToken
     * @return
     */
    public String getPass(String accessToken) {
        String url = dynamicPasswordProperties.getHost() + "/api/ctsp/cpam/cpam-token-decrypt/token/get-access";
        JSONObject param = new JSONObject();
        param.set("keyId", keyId);
        param.set("ak", AK);
        param.set("sk", SK);
        param.set("skVersion", skVersion);
        param.set("productCode", productCode);
        param.set("token", token);
        param.set("institutionNumber", institutionNumber);
        JSONObject out = httppost(url, param.toString(), accessToken);
        if (out.getStr("code").equals("0")) {
            out = out.getJSONObject("result");
            JSONObject data = out.getJSONObject("data");
            String accessWord = data.getStr("accessWord");
            System.out.println(accessToken);
            return accessWord;
        } else {
            System.out.println("获取token失败！" + out);
            return null;
        }
    }

}
